package com.jj.service;

import org.slf4j.Logger;
import org.springframework.stereotype.Component;

import java.util.Calendar;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArraySet;

import static org.slf4j.LoggerFactory.getLogger;

/**
 * 客户端推送帧号，服务端不再需要Timer来做tick
 * @author luhan
 * @date 2018.1.12
 */
@Component
public class TickerWorkerThread extends Thread{
    private static Logger logger = getLogger(TickerWorkerThread.class);

    private static final Float FRAME_PER_SECOND = 60.0f;
    private static final Float interval = 1000.0f/FRAME_PER_SECOND;

    private volatile Integer _second = 0;
    private volatile Integer _cnt = 0;

    private CopyOnWriteArraySet<Ticker> tickerSet = new CopyOnWriteArraySet<>();

    {
        setDaemon(true);
    }

    public void addTicker(Ticker ticker){
        tickerSet.add(ticker);
    }

    public void removeTicker(Ticker ticker){
        tickerSet.remove(ticker);
    }

    @Override
    public void run() {
        while(true){
            //这里是真正意义上的主循环，要记录时间
            //真正意义上的开始时间，0点
            //经过时间

            //用于记录游戏渲染的时间
            long update=0;
            //用于记录游戏线程睡眠的时间
            long sleep=0;

            //如果想要很好的中断游戏线程，可以把true变为自己规定的boolean变量，通过改变变量值中断游戏
            while(true){
                while(getSecondsCnt() >= FRAME_PER_SECOND.intValue()){
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                //得到当前时间点，注意nanoTime（）得到的是个时间点其数值并不是实际意义上的时间
                long before=System.nanoTime();
                //用于记录上一帧花费的时间
                long t=sleep+update;

                //游戏界面的更新和渲染，该方法是自己在主界面中定义的
                //begin process.
                Iterator<Ticker> it = tickerSet.iterator();
                while(it.hasNext()){
                    Ticker ticker = it.next();
                    try{
                        ticker.tick();
                    }catch (Throwable tt){
                        logger.error("exception", tt);
                    }
                }
                //end of process

                int cnt = getAndAddSeconds();
                update=(System.nanoTime()-before)/1000000L;
                //这里是计算渲染的总共时间
                sleep=Math.max(2,(interval.intValue() - update));

                //logger.debug("I'm working {}, sleep:{}, cnt:{}",
                //        System.currentTimeMillis(), sleep, cnt);

                //因为我们规定了每一帧的睡眠时间是
                //16毫秒，所以在2和渲染后还剩余的时间中取个最大值作为睡眠时间，设2是为了防止渲染时间太长
                try {
                    Thread.sleep(sleep);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }


            }
        }
    }

    public Integer getSecnds(){
        return Calendar.getInstance().get(Calendar.SECOND);
    }

    public Integer getSecondsCnt(){
        Integer second = getSecnds();
        if(_second.equals(second)){
            return _cnt;
        }else{
            _second = second;
            _cnt = 0;
        }
        return _cnt;
    }

    public Integer getAndAddSeconds(){
        Integer second = getSecnds();
        if(_second.equals(second)){
            _cnt++;
        }else{
            _second = second;
            _cnt = 1;
        }
        return _cnt;
    }
}
